/*
 * BottomBar library for Android
 * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.roughike.bottombar;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import ohos.agp.animation.Animator;
import ohos.agp.colors.RgbColor;
import ohos.agp.colors.RgbPalette;
import ohos.agp.components.Attr;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.Text;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Paint;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.window.dialog.ToastDialog;
import ohos.app.Context;

/**
 * 外层容器
 */
public class BottomBar extends DirectionalLayout implements Component.ClickedListener, Component.LongClickedListener, Component.LayoutRefreshedListener {
    private static final float DEFAULT_INACTIVE_SHIFTING_TAB_ALPHA = 0.6f;

    /** normal behavior */
    public static final int BEHAVIOR_NONE = 0;

    /** shifting behavior */
    public static final int BEHAVIOR_SHIFTING = 1;

    /** icons only behavior */
    public static final int BEHAVIOR_ICONS_ONLY = 8;

    private BatchTabPropertyApplier batchPropertyApplier;
    private int primaryColor;
    private int screenWidth;
    private int tenDp;
    private int maxFixedItemWidth;

    // XML Attributes
    private String tabXmlResourcePath;
    private boolean isTabletMode;
    private int behaviors;
    private float inActiveTabAlpha;
    private float activeTabAlpha;
    private int inActiveTabColor;
    private int activeTabColor;
    private int badgeBackgroundColor;
    private boolean hideBadgeWhenActive;
    private boolean longPressHintsEnabled;
    private Font titleTypeFace;

    private Component backgroundOverlay;
    private ComponentContainer outerContainer;
    private ComponentContainer tabContainer;

    private int defaultBackgroundColor = Color.WHITE.getValue();
    private int currentBackgroundColor;
    private int currentTabPosition;

    private int inActiveShiftingItemWidth;
    private int activeShiftingItemWidth;

    private TabSelectionInterceptor tabSelectionInterceptor;

    private OnTabSelectListener onTabSelectListener;

    private OnTabReselectListener onTabReselectListener;

    private boolean isComingFromRestoredState;
    private boolean ignoreTabReselectionListener;

    private BottomBarTab[] currentTabs;

    public BottomBar(Context context) {
        this(context, null);
    }

    public BottomBar(Context context, AttrSet attrs) {
        this(context, attrs, null);
    }

    public BottomBar(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttrSet attrs, String defStyleAttr) {
        batchPropertyApplier = new BatchTabPropertyApplier(this);
        populateAttributes(context, attrs, defStyleAttr);
        initializeViews();
        determineInitialBackgroundColor();
        setItems(tabXmlResourcePath);
        setLayoutRefreshedListener(this);
    }

    @SuppressWarnings("all")
    private void populateAttributes(Context context, AttrSet attrs, String defStyleAttr) {
        primaryColor = ResHelper.invokeResId(context, "$color:colorPrimary");
        if (primaryColor == -1) {
            throw new IllegalStateException("color.json must be has colorPrimary element!");
        }
        screenWidth = ResHelper.getScreenWidth(getContext());
        tenDp = ResHelper.dpToPixel(getContext(), 10);
        maxFixedItemWidth = ResHelper.dpToPixel(getContext(), 168);
        Optional<Attr> bb_tabXmlResource = attrs.getAttr("bb_tabXmlResource");
        tabXmlResourcePath = bb_tabXmlResource.isPresent()?bb_tabXmlResource.get().getStringValue():"";
        Optional<Attr> bb_tabletMode = attrs.getAttr("bb_tabletMode");
        isTabletMode = bb_tabletMode.isPresent() ? bb_tabletMode.get().getBoolValue() : false;
        Optional<Attr> bb_behavior = attrs.getAttr("bb_behavior");
        if (bb_behavior.isPresent()) {
            switch (bb_behavior.get().getStringValue()) {
                case "shifting":
                    behaviors = BEHAVIOR_SHIFTING;
                    break;
                case "iconOnly":
                    behaviors = BEHAVIOR_ICONS_ONLY;
                    break;
                case "shifting|iconOnly":
                    behaviors = BEHAVIOR_SHIFTING|BEHAVIOR_ICONS_ONLY;
                    break;
                default:
                    behaviors = BEHAVIOR_NONE;
            }
        } else {
            behaviors = BEHAVIOR_NONE;
        }
        Optional<Attr> bb_longPressHintsEnabled = attrs.getAttr("bb_longPressHintsEnabled");
        longPressHintsEnabled = bb_longPressHintsEnabled.isPresent()?bb_longPressHintsEnabled.get().getBoolValue():true;
        Optional<Attr> bb_titleTypeFace = attrs.getAttr("bb_titleTypeFace");
        String titleTypeFacePath = bb_titleTypeFace.isPresent() ? bb_titleTypeFace.get().getStringValue() : "";
        titleTypeFace = new Font.Builder(ResHelper.resPathToFile(context, titleTypeFacePath)).makeItalic(true).setWeight(Font.BOLD).build();

        // global attr for tabs
        Optional<Attr> bb_inActiveTabAlpha = attrs.getAttr("bb_inActiveTabAlpha");
        inActiveTabAlpha = bb_inActiveTabAlpha.isPresent()? bb_inActiveTabAlpha.get().getFloatValue() :(isShiftingMode() ? DEFAULT_INACTIVE_SHIFTING_TAB_ALPHA : 1);
        Optional<Attr> bb_activeTabAlpha = attrs.getAttr("bb_activeTabAlpha");
        activeTabAlpha = bb_activeTabAlpha.isPresent()? bb_activeTabAlpha.get().getFloatValue():1;
        int defaultInActiveColor = isShiftingMode() ? Color.WHITE.getValue() : ResHelper.getColor(context, ResourceTable.Color_bb_inActiveBottomBarItemColor);
        int defaultActiveColor = isShiftingMode() ? Color.WHITE.getValue() : primaryColor;
        Optional<Attr> bb_inActiveTabColor = attrs.getAttr("bb_inActiveTabColor");
        inActiveTabColor = bb_inActiveTabColor.isPresent()? bb_inActiveTabColor.get().getColorValue().getValue() :defaultInActiveColor;
        Optional<Attr> bb_activeTabColor = attrs.getAttr("bb_activeTabColor");
        activeTabColor = bb_activeTabColor.isPresent()? bb_activeTabColor.get().getColorValue().getValue() :defaultActiveColor;
        Optional<Attr> bb_badgeBackgroundColor = attrs.getAttr("bb_badgeBackgroundColor");
        badgeBackgroundColor = bb_badgeBackgroundColor.isPresent()? bb_badgeBackgroundColor.get().getColorValue().getValue():Color.RED.getValue();
        Optional<Attr> bb_badgesHideWhenActive = attrs.getAttr("bb_badgesHideWhenActive");
        hideBadgeWhenActive = bb_badgesHideWhenActive.isPresent()? bb_badgesHideWhenActive.get().getBoolValue():true;
    }

    private boolean isShiftingMode() {
        return !isTabletMode && hasBehavior(BEHAVIOR_SHIFTING);
    }


    private boolean isIconsOnlyMode() {
        return !isTabletMode && hasBehavior(BEHAVIOR_ICONS_ONLY);
    }

    private boolean hasBehavior(int behavior) {
        return (behaviors | behavior) == behaviors;
    }

    private void initializeViews() {
        // xml中<merge>标签替代方案
        ComponentContainer tempContainer = (ComponentContainer) LayoutScatter.getInstance(getContext()).parse(
                isTabletMode ? ResourceTable.Layout_bb_bottom_bar_item_container_tablet : ResourceTable.Layout_bb_bottom_bar_item_container, null, false);
        ArrayList<Component> childs = new ArrayList<>();
        for (int i = 0; i < tempContainer.getChildCount(); i++) {
            Component child = tempContainer.getComponentAt(i);
            childs.add(child);
        }
        tempContainer.removeAllComponents();
        for (Component child : childs) {
            this.addComponent(child);
        }
        this.setLayoutConfig(new LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT));
        this.setOrientation(isTabletMode?HORIZONTAL:VERTICAL);

        backgroundOverlay = findComponentById(ResourceTable.Id_bb_bottom_bar_background_overlay);
        outerContainer = (ComponentContainer) findComponentById(ResourceTable.Id_bb_bottom_bar_outer_container);
        tabContainer = (ComponentContainer) findComponentById(ResourceTable.Id_bb_bottom_bar_item_container);

    }

    private void determineInitialBackgroundColor() {
        if (isShiftingMode()) {
            defaultBackgroundColor = primaryColor;
        }

        Element userDefinedBackground = getBackgroundElement();

        boolean userHasDefinedBackgroundColor = userDefinedBackground != null
                && userDefinedBackground instanceof ShapeElement;

        if (userHasDefinedBackgroundColor) {
            defaultBackgroundColor = ((ShapeElement) userDefinedBackground).getRgbColors()[0].asRgbaInt();
            ShapeElement element = new ShapeElement();
            element.setRgbColor(RgbPalette.TRANSPARENT);
            setBackground(element);
        }

    }

    /**
     * Set the items for the BottomBar from XML Resource.
     *
     * @param xmlResPath like "resources/rawfile/xml/customtabs.xml", xml file must be in rawfile dir
     */
    public void setItems(String xmlResPath) {
        setItems(xmlResPath, null);
    }

    /**
     * Set the item for the BottomBar from XML Resource with a default configuration
     * for each tab.
     *
     * @param xmlResPath like "resources/rawfile/xml/customtabs.xml", xml file must be in rawfile dir
     * @param defaultTabConfig default tabConfig
     */
    public void setItems(String xmlResPath, BottomBarTab.Config defaultTabConfig) {

        if (defaultTabConfig == null) {
            defaultTabConfig = getTabConfig();
        }

        TabParser parser = new TabParser(getContext(), defaultTabConfig, xmlResPath);
        updateItems(parser.parseTabs());
    }

    private BottomBarTab.Config getTabConfig() {
        return new BottomBarTab.Config.Builder()
                .inActiveTabAlpha(inActiveTabAlpha)
                .activeTabAlpha(activeTabAlpha)
                .inActiveTabColor(inActiveTabColor)
                .activeTabColor(activeTabColor)
                .badgeBackgroundColor(badgeBackgroundColor)
                .hideBadgeWhenSelected(hideBadgeWhenActive)
                .barColorWhenSelected(defaultBackgroundColor)
                .titleTypeFace(titleTypeFace)
                .build();
    }

    private void updateItems(final List<BottomBarTab> bottomBarItems) {
        tabContainer.removeAllComponents();

        int index = 0;

        BottomBarTab[] viewsToAdd = new BottomBarTab[bottomBarItems.size()];

        for (BottomBarTab bottomBarTab : bottomBarItems) {
            BottomBarTab.Type type;

            if (isShiftingMode()) {
                type = BottomBarTab.Type.SHIFTING;
            } else if (isTabletMode) {
                type = BottomBarTab.Type.TABLET;
            } else {
                type = BottomBarTab.Type.FIXED;
            }

            if (isIconsOnlyMode()) {
                bottomBarTab.setIsTitleless(true);
            }

            bottomBarTab.setType(type);
            bottomBarTab.prepareLayout();

            if (index == currentTabPosition) {
                bottomBarTab.select(false);

                handleBackgroundColorChange(bottomBarTab, false);
            } else {
                bottomBarTab.deselect(false);
            }

            if (!isTabletMode) {

                viewsToAdd[index] = bottomBarTab;
            } else {
                tabContainer.addComponent(bottomBarTab);
            }

            bottomBarTab.setClickedListener(this);
            bottomBarTab.setLongClickedListener(this);
            index++;
        }

        currentTabs = viewsToAdd;

        if (!isTabletMode) {
            resizeTabsToCorrectSizes(viewsToAdd);
        }
    }

    private void resizeTabsToCorrectSizes(BottomBarTab[] tabsToAdd) {
        int viewWidth = ResHelper.pixelToDp(getContext(), getWidth());

        if (viewWidth <= 0 || viewWidth > screenWidth) {
            viewWidth = screenWidth;
        }

        int proposedItemWidth = Math.min(
                ResHelper.dpToPixel(getContext(), (float) viewWidth / tabsToAdd.length),
                maxFixedItemWidth
        );

        inActiveShiftingItemWidth = (int) (proposedItemWidth * 0.9f);
        activeShiftingItemWidth = (int) (proposedItemWidth + (proposedItemWidth * ((tabsToAdd.length - 1) * 0.1f)));
        int height = ResHelper.dpToPixel(getContext(),ResHelper.getDimension(getContext(), ResourceTable.Integer_bb_height));

        for (BottomBarTab tabView : tabsToAdd) {
            ComponentContainer.LayoutConfig params = tabView.getLayoutConfig();
            params.height = height;

            if (isShiftingMode()) {
                if (tabView.isActive()) {
                    params.width = activeShiftingItemWidth;
                } else {
                    params.width = inActiveShiftingItemWidth;
                }
            } else {
                params.width = proposedItemWidth;
            }

            if (tabView.getComponentParent() == null) {
                tabContainer.addComponent(tabView);
            }

            tabView.setLayoutConfig(params);
        }
    }

    /**
     * Set a listener that gets fired when the selected {@link BottomBarTab} is about to change.
     *
     * @param interceptor a listener for potentially interrupting changes in tab selection.
     */
    public void setTabSelectionInterceptor(TabSelectionInterceptor interceptor) {
        tabSelectionInterceptor = interceptor;
    }

    /**
     * Removes the current {@link TabSelectionInterceptor} listener
     */
    public void removeOverrideTabSelectionListener() {
        tabSelectionInterceptor = null;
    }

    /**
     * Set a listener that gets fired when the selected {@link BottomBarTab} changes.
     * <p>
     * Note: Will be immediately called for the currently selected tab
     * once when set.
     *
     * @param listener a listener for monitoring changes in tab selection.
     */
    public void setOnTabSelectListener(OnTabSelectListener listener) {
        setOnTabSelectListener(listener, true);
    }

    /**
     * Set a listener that gets fired when the selected {@link BottomBarTab} changes.
     * <p>
     * If {@code shouldFireInitially} is set to false, this listener isn't fired straight away
     * it's set, but you'll get all events normally for consecutive tab selection changes.
     *
     * @param listener            a listener for monitoring changes in tab selection.
     * @param shouldFireInitially whether the listener should be fired the first time it's set.
     */
    public void setOnTabSelectListener(OnTabSelectListener listener, boolean shouldFireInitially) {
        onTabSelectListener = listener;

        if (shouldFireInitially && getTabCount() > 0) {
            listener.onTabSelected(getCurrentTabTag());
        }
    }

    /**
     * Removes the current {@link OnTabSelectListener} listener
     */
    public void removeOnTabSelectListener() {
        onTabSelectListener = null;
    }

    /**
     * Set a listener that gets fired when a currently selected {@link BottomBarTab} is clicked.
     *
     * @param listener a listener for handling tab reselections.
     */
    public void setOnTabReselectListener(OnTabReselectListener listener) {
        onTabReselectListener = listener;
    }

    /**
     * Removes the current {@link OnTabReselectListener} listener
     */
    public void removeOnTabReselectListener() {
        onTabReselectListener = null;
    }

    /**
     * Set the default selected to be the tab with the corresponding tab id.
     * By default, the first tab in the container is the default tab.
     *
     * @param defaultIdtag defaultIdtag
     */
    public void setDefaultTab(String defaultIdtag) {
        int defaultTabPosition = findPositionForTabWithId(defaultIdtag);
        setDefaultTabPosition(defaultTabPosition);
    }

    /**
     * Sets the default tab for this BottomBar that is shown until the user changes
     * the selection.
     *
     * @param defaultTabPosition the default tab position.
     */
    public void setDefaultTabPosition(int defaultTabPosition) {
        if (isComingFromRestoredState) {
            return;
        }

        selectTabAtPosition(defaultTabPosition);
    }

    /**
     * Select the tab with the corresponding id.
     *
     * @param tabIdtag the string type id of the tab
     */
    public void selectTabWithId(String tabIdtag) {
        int tabPosition = findPositionForTabWithId(tabIdtag);
        selectTabAtPosition(tabPosition);
    }

    /**
     * Select a tab at the specified position.
     *
     * @param position the position to select.
     */
    public void selectTabAtPosition(int position) {
        selectTabAtPosition(position, false);
    }

    /**
     * Select a tab at the specified position.
     *
     * @param position the position to select.
     * @param animate  should the tab change be animated or not.
     */
    public void selectTabAtPosition(int position, boolean animate) {
        if (position > getTabCount() - 1 || position < 0) {
            throw new IndexOutOfBoundsException("Can't select tab at position " +
                    position + ". This BottomBar has no items at that position.");
        }

        BottomBarTab oldTab = getCurrentTab();
        BottomBarTab newTab = getTabAtPosition(position);

        oldTab.deselect(animate);
        newTab.select(animate);

        updateSelectedTab(position);
        shiftingMagic(oldTab, newTab, animate);
        handleBackgroundColorChange(newTab, animate);
    }

    /**
     * get the tabs count
     *
     * @return tabs count
     */
    public int getTabCount() {
        return tabContainer.getChildCount();
    }

    /**
     * Get the currently selected tab.
     *
     * @return an instance of the current BottomBarTab
     */
    public BottomBarTab getCurrentTab() {
        return getTabAtPosition(getCurrentTabPosition());
    }

    /**
     * Get the tab at the specified position.
     *
     * @param position the position of tab
     * @return a BottomBarTab obj
     */
    public BottomBarTab getTabAtPosition(int position) {
        Component child = tabContainer.getComponentAt(position);

        if (child instanceof BadgeContainer) {
            return findTabInLayout((BadgeContainer) child);
        }

        return (BottomBarTab) child;
    }

    /**
     * Get the resource id for the currently selected tab.
     *
     * @return the current tab tag
     */
    public String getCurrentTabTag() {
        return (String) getCurrentTab().getTag();
    }

    /**
     * Get the currently selected tab position.
     *
     * @return the current tab position
     */
    public int getCurrentTabPosition() {
        return currentTabPosition;
    }

    /**
     * Find the tabs' position in the container by id.
     *
     * @param idtag idtag
     * @return position of BottomBarTab
     */
    public int findPositionForTabWithId(String idtag) {
        return getTabWithIdtag(idtag).getIndexInTabContainer();
    }

    /**
     * Find a BottomBarTab with the corresponding idtag.
     *
     * @param idtag the id for BottomBarTab
     * @return a BottomBarTab obj
     */
    public BottomBarTab getTabWithIdtag(String idtag) {
        BottomBarTab bbt = null;
        for (int i = 0; i < tabContainer.getChildCount(); i++) {
            Component componentAt = tabContainer.getComponentAt(i);
            if (idtag.equals(componentAt.getTag().toString())) {
                bbt = (BottomBarTab)componentAt;
                break;
            }

        }
        return bbt;
    }
    /**
     * Controls whether the long pressed tab title should be displayed in
     * a helpful Toast if the title is not currently visible.
     *
     * @param enabled true if toasts should be shown to indicate the title
     *                of a long pressed tab, false otherwise.
     */
    public void setLongPressHintsEnabled(boolean enabled) {
        longPressHintsEnabled = enabled;
    }

    /**
     * Set alpha value used for inactive BottomBarTabs.
     *
     * @param alpha an alpha value
     */
    public void setInActiveTabAlpha(float alpha) {
        inActiveTabAlpha = alpha;

        batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() {
            @Override
            public void update(BottomBarTab tab) {
                tab.setInActiveAlpha(inActiveTabAlpha);
            }
        });
    }

    /**
     * Set alpha value used for active BottomBarTabs.
     *
     * @param alpha an alpha value
     */
    public void setActiveTabAlpha(float alpha) {
        activeTabAlpha = alpha;

        batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() {
            @Override
            public void update(BottomBarTab tab) {
                tab.setActiveAlpha(activeTabAlpha);
            }
        });
    }

    /**
     * Set inactive color used for unselected BottomBarTabs.
     *
     * @param color must be argb color
     */
    public void setInActiveTabColor(int color) {
        inActiveTabColor = color;

        batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() {
            @Override
            public void update(BottomBarTab tab) {
                tab.setInActiveColor(inActiveTabColor);
            }
        });
    }

    /**
     * Set active color used for selected BottomBarTabs.
     *
     * @param color must be argb color
     */
    public void setActiveTabColor(int color) {
        activeTabColor = color;

        batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() {
            @Override
            public void update(BottomBarTab tab) {
                tab.setActiveColor(activeTabColor);
            }
        });
    }

    /**
     * Set background color for the badge.
     *
     * @param color must be argb color
     */
    public void setBadgeBackgroundColor(int color) {
        badgeBackgroundColor = color;

        batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() {
            @Override
            public void update(BottomBarTab tab) {
                tab.setBadgeBackgroundColor(badgeBackgroundColor);
            }
        });
    }

    /**
     * Controls whether the badge (if any) for active tabs
     * should be hidden or not.
     *
     * @param hideWhenSelected if true,the badge will hide when selected
     */
    public void setBadgesHideWhenActive(final boolean hideWhenSelected) {
        hideBadgeWhenActive = hideWhenSelected;
        batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() {
            @Override
            public void update(BottomBarTab tab) {
                tab.setBadgeHidesWhenActive(hideWhenSelected);
            }
        });
    }

    /**
     * Set a custom typeface for all tab's titles.
     *
     * @param typeface a Font obj of the custom typeface
     */
    public void setTabTitleTypeface(Font typeface) {
        titleTypeFace = typeface;
        batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() {
            @Override
            public void update(BottomBarTab tab) {
                tab.setTitleTypeface(titleTypeFace);
            }
        });
    }

    @Override
    public void onRefreshed(Component component) {
        if (!isTabletMode) {
            resizeTabsToCorrectSizes(currentTabs);
        }
        updateTitleBottomPadding();
    }

    private void updateTitleBottomPadding() {
        if (isIconsOnlyMode()) {
            return;
        }
        int tabCount = getTabCount();
        if (tabContainer == null || tabCount == 0 || !isShiftingMode()) {
            return;
        }

        for (int i = 0; i < tabCount; i++) {
            BottomBarTab tab = getTabAtPosition(i);
            Text title = tab.getTitleView();

            if (title == null) {
                continue;
            }

            Paint paint = new Paint();
            paint.setFont(title.getFont());
            Paint.FontMetrics fontMetrics = paint.getFontMetrics();
            int baseline = Math.round((fontMetrics.descent-fontMetrics.ascent)/2);
            int height = title.getHeight();

            int paddingInsideTitle = height - baseline;
            int missingPadding = tenDp - paddingInsideTitle;

            if (missingPadding > 0) {
                title.setPadding(title.getPaddingLeft(), title.getPaddingTop(),
                        title.getPaddingRight(), missingPadding + title.getPaddingBottom());
            }
        }
    }



    @Override
    public void onClick(Component component) {
        if (!(component instanceof BottomBarTab)) {
            return;
        }
        handleClick((BottomBarTab) component);
    }

    @Override
    public void onLongClicked(Component component) {
        if(component instanceof BottomBarTab) {
            handleLongClick((BottomBarTab) component);
        }
    }

    private BottomBarTab findTabInLayout(ComponentContainer child) {
        for (int i = 0; i < child.getChildCount(); i++) {
            Component candidate = child.getComponentAt(i);
            if (candidate instanceof BottomBarTab) {
                return (BottomBarTab) candidate;
            }
        }
        return null;
    }

    private void handleClick(BottomBarTab newTab) {
        BottomBarTab oldTab = getCurrentTab();

        if (tabSelectionInterceptor != null
                && tabSelectionInterceptor.shouldInterceptTabSelection(oldTab.getTag().toString(), newTab.getTag().toString())) {
            return;
        }

        oldTab.deselect(true);
        newTab.select(true);

        shiftingMagic(oldTab, newTab, true);
        handleBackgroundColorChange(newTab, true);
        updateSelectedTab(newTab.getIndexInTabContainer());
    }

    private boolean handleLongClick(BottomBarTab longClickedTab) {
        boolean areInactiveTitlesHidden = isShiftingMode() || isTabletMode;
        boolean isClickedTitleHidden = !longClickedTab.isActive();
        boolean shouldShowHint = areInactiveTitlesHidden
                && isClickedTitleHidden
                && longPressHintsEnabled;

        if (shouldShowHint) {
            ToastDialog toastDialog = new ToastDialog(getContext());
            toastDialog.setText(longClickedTab.getTitle());
            toastDialog.setDuration(1000);
            toastDialog.show();
        }
        return true;
    }

    private void updateSelectedTab(int newPosition) {
        String newIdtag = getTabAtPosition(newPosition).getTag().toString();
        if (newPosition != currentTabPosition) {
            if (onTabSelectListener != null) {
                onTabSelectListener.onTabSelected(newIdtag);
            }
        } else if (onTabReselectListener != null && !ignoreTabReselectionListener) {
            onTabReselectListener.onTabReSelected(newIdtag);
        }

        currentTabPosition = newPosition;

        if (ignoreTabReselectionListener) {
            ignoreTabReselectionListener = false;
        }
    }

    private void shiftingMagic(BottomBarTab oldTab, BottomBarTab newTab, boolean animate) {
        if (isShiftingMode()) {
            oldTab.updateWidth(inActiveShiftingItemWidth, animate);
            newTab.updateWidth(activeShiftingItemWidth, animate);
        }
    }

    private void handleBackgroundColorChange(BottomBarTab tab, boolean animate) {
        int newColor = tab.getBarColorWhenSelected();

        if (currentBackgroundColor == newColor) {
            return;
        }

        if (!animate) {
            ShapeElement element = new ShapeElement();
            element.setRgbColor(RgbColor.fromArgbInt(newColor));
            outerContainer.setBackground(element);
            return;
        }

        Component clickedView = tab;

        if (tab.hasActiveBadge()) {
            clickedView = tab.getOuterView();
        }

        animateBGColorChange(clickedView, newColor);
        currentBackgroundColor = newColor;
    }

    private void animateBGColorChange(Component clickedView, final int newColor) {
        prepareForBackgroundColorAnimation(newColor);

        backgroundCrossfadeAnimation(newColor);
    }

    private void prepareForBackgroundColorAnimation(int newColor) {
        ShapeElement element = new ShapeElement();
        element.setRgbColor(RgbColor.fromArgbInt(newColor));
        backgroundOverlay.setBackground(element);
        backgroundOverlay.setVisibility(Component.VISIBLE);
    }

    private void backgroundCrossfadeAnimation(final int newColor) {
        backgroundOverlay.setAlpha(0);
        backgroundOverlay.createAnimatorProperty()
                .alpha(1)
                .setStateChangedListener(new Animator.StateChangedListener() {
                    @Override
                    public void onStart(Animator animator) { }
                    @Override
                    public void onStop(Animator animator) { }
                    @Override
                    public void onCancel(Animator animator) {
                        onEnd(animator);
                    }

                    @Override
                    public void onEnd(Animator animator) {
                        ShapeElement element = new ShapeElement();
                        element.setRgbColor(RgbColor.fromArgbInt(newColor));
                        outerContainer.setBackground(element);
                        backgroundOverlay.setVisibility(Component.INVISIBLE);
                        backgroundOverlay.setAlpha(1);
                    }

                    @Override
                    public void onPause(Animator animator) { }
                    @Override
                    public void onResume(Animator animator) { }
                })
                .start();
    }
}
